import pygame  # 导入pygame模块
import random  # 导入随机数模块
import _thread  # 导入多线程模块
from time import sleep  # 从时间模块中导入延迟函数
import sys  # 导入系统模块

FILE_PATH = sys.path[0]  # 获取当前文件路径

WINDOW_SIZE = [600, 600]  # 设置窗口大小
TITLE = "christmasTree"  # 设置窗口名称
TREE_HEIGHT = 400  # 设置圣诞树的树高
TREE_WEITH = 5  # 设置圣诞树的树宽变化量
TREE_COLOR = "#01df74"  # 设置树干和树叶的颜色
TREE_BALL_COLORS = ["#ff5319", "#a9e2f3",
                    "#2efe64", "#ff1953"]  # 设置圣诞树上的球的颜色，按顺序变化
BACK_COLOR = "black"  # 设置背景颜色
TRE_BALL_RADIUS = 8  # 设置圣诞树上的球的直径
SPACING = 30  # 设置每两个树叶间起始点的间距
TRUNK_WIDTH = 10  # 设置圣诞树树干的宽度
LEAF_WIDTH = 10  # 设置树叶的线的粗细
SNOW_SIZE_MAX = 4  # 设置背景中雪花的最大半径
SNOW_SIZE_MIN = 2  # 设置背景中雪花的最小半径
SNOW_NUM_MAX = 100  # 设置背景中雪花数量的最大值
SNOW_TIME = 0.07  # 设置雪花坐标更改的间隔时间
LEAF_PRO = .8  # 设置树叶所占整棵树的比例，范围0-1，数据类型：float
BALL_CHANGE_CALOR_TIME = 0.5  # 设置树上的球更改颜色的间隔时间
SNOW_RANDOM_COLORS = ["#ffffff", "#ddf3fa"]  # 设置雪花的随机颜色
BASE_THICKNESS = 20  # 设置树的底座厚度
BASE_RADIUS = 10  # 设置底座中圆的半径
BASE_COLORS = ["#b40404", "#b43104", "#df7401", "#c59801"]  # 设置底座的随机颜色
BASE_BALL_NUM = 200  # 设置底座中圆的数量
BASE_CHANGE_TIME = 0.5  # 设置底座颜色更改的间隔时间
BASE_WIDTH = .4  # 设置底座宽度相较于整棵树的最大宽度的占比
TEXT = "Merry Christmas"  # 设置底座上方的文字
TEXT_2 = ""  # 设置窗口顶端的文字
FONT_SIZE = 40  # 设置底座上方文字的字体大小
FONT_SIZE_2 = 60  # 设置窗口顶端的文字的字体大小
FONT = FILE_PATH+"\segoesc.ttf"  # 使用外部字体库的文件路径，默认在该文件所在文件夹下查找字体文件
TEXT_COLOR = "#f5fb69"  # 设置文字颜色
MUSIC = FILE_PATH+"/music.mp3"  # 设置在程序运行时播放的背景音乐，默认在该文件所在文件夹下查找文件

ANGLE = 10**(1/2)  # 树叶形状控制变量
WINDOW_CENTER_POINT = [int(WINDOW_SIZE[0] / 2),
                       int(WINDOW_SIZE[1] / 2)]  # 自动计算窗口中心点位
TRUNK = int(TREE_HEIGHT * (1 - LEAF_PRO))  # 自动计算树干占整棵树的比例，并根据树高求出树干长度
LEAF = int(TREE_HEIGHT * LEAF_PRO)  # 自动计算树叶占比，并根据树高求出树叶高度
TREE_LOOP_NUM = int(LEAF/SPACING)  # 自动根据树高和占比计算需要进行多少次循环，以按照相应比例画出相应的树叶数量
TREE_BOTTOM_POINT = [WINDOW_CENTER_POINT[0],
                     WINDOW_CENTER_POINT[1] + TREE_HEIGHT/2]  # 自动计算整棵树的最低点的位置
TREE_TOP_POINT = [WINDOW_CENTER_POINT[0],
                  WINDOW_CENTER_POINT[1]-TREE_HEIGHT/2]  # 自动计算整棵树的最高点的位置
RANDOM_COLOR = TREE_BALL_COLORS[0]  # 树上的球的颜色控制变量，可以设置为定值，但小球颜色将与上方的颜色组无关
BASE_WIDTH_NUM = int((abs((TREE_TOP_POINT[0]-ANGLE*TREE_WEITH*(TREE_LOOP_NUM+1)) -
                          (TREE_TOP_POINT[0]+ANGLE*TREE_WEITH*(TREE_LOOP_NUM+1)))*(1-BASE_WIDTH))/2)  # 通过上方的各种参数，计算底座的宽度
TEXT_CENTER_POINT = [TREE_BOTTOM_POINT[0],
                     TREE_BOTTOM_POINT[1]-5]  # 设置底座上方的文字的中心点所在位置
done = False  # 判断是否退出程序

pygame.init()  # pygame模块初始化
screen = pygame.display.set_mode(size=WINDOW_SIZE)  # 设置pygame窗口大小和窗口模式
pygame.display.set_caption(TITLE)  # 设置pygame窗口标题
screen.fill(BACK_COLOR)  # 填充背景颜色


def draw_teee():
    """
    |此函数需通过多线程或多进程启动，其在运行过程中会形成阻塞，无法使本函数之后的其他代码运行|
    本函数功能是绘制圣诞树以及圣诞树上的小球
    """
    while not done:  # 通过多线程进行绘画，固需要循环进行重复绘制
        for i in range(TREE_LOOP_NUM):  # 通过前面计算的循环次数来绘制树叶
            pygame.draw.line(screen, TREE_COLOR, [TREE_TOP_POINT[0], TREE_TOP_POINT[1] + SPACING * i], [
                TREE_TOP_POINT[0] + ANGLE * TREE_WEITH * (i + 1), TREE_TOP_POINT[1] + (TREE_WEITH + SPACING) * (i + 1)], LEAF_WIDTH)  # 绘制树干右侧的树叶的上半部分
            pygame.draw.line(screen, TREE_COLOR, [TREE_TOP_POINT[0], TREE_TOP_POINT[1] + SPACING * i + SPACING / 2], [
                TREE_TOP_POINT[0] + ANGLE * TREE_WEITH * (i+1), TREE_TOP_POINT[1] + (TREE_WEITH + SPACING) * (i + 1)], LEAF_WIDTH)  # 绘制树干右侧的树叶的下半部分

            pygame.draw.line(screen, TREE_COLOR, [TREE_TOP_POINT[0], TREE_TOP_POINT[1]+SPACING*i], [
                TREE_TOP_POINT[0]-ANGLE*TREE_WEITH*(i+1), TREE_TOP_POINT[1]+(TREE_WEITH+SPACING)*(i+1)], LEAF_WIDTH)  # 绘制树干左侧的树叶的上半部分
            pygame.draw.line(screen, TREE_COLOR, [TREE_TOP_POINT[0], TREE_TOP_POINT[1]+SPACING*i+SPACING/2], [
                TREE_TOP_POINT[0]-ANGLE*TREE_WEITH*(i+1), TREE_TOP_POINT[1]+(TREE_WEITH+SPACING)*(i+1)], LEAF_WIDTH)  # 绘制树干左侧的树叶的下半部分

            pygame.draw.circle(screen, RANDOM_COLOR, [TREE_TOP_POINT[0] + ANGLE * TREE_WEITH * (
                i + 1), TREE_TOP_POINT[1] + (TREE_WEITH + SPACING) * (i + 1)+2], TRE_BALL_RADIUS)  # 绘制树叶右侧的小球
            pygame.draw.circle(screen, RANDOM_COLOR, [TREE_TOP_POINT[0]-ANGLE*TREE_WEITH*(
                i+1), TREE_TOP_POINT[1]+(TREE_WEITH+SPACING)*(i+1)+2], TRE_BALL_RADIUS)  # 绘制树叶左侧的小球

        pygame.draw.line(screen, TREE_COLOR, TREE_TOP_POINT,
                         TREE_BOTTOM_POINT, TRUNK_WIDTH)  # 绘制树干
        pygame.draw.circle(screen, RANDOM_COLOR,
                           TREE_TOP_POINT, TRE_BALL_RADIUS)  # 绘制圣诞树顶端的小球
        draw_font()  # 调用字体绘制函数来绘制文字
        pygame.display.update()  # 窗口刷新
    pygame.quit()  # 窗口退出


def clean_snow(point: list):
    """
    此函数可以通过传入的记录点来清除雪花或圆
    原理是通过用与背景色相同的颜色覆盖与背景色颜色不同的部分来达到区域清除的效果
    point为一个列表，其中应当有N个[x,y,rad]
        x为需要覆盖的圆的圆心的横坐标
        y为需要覆盖的圆的圆心的纵坐标
        rad为需要覆盖的圆的半径
    """
    for i in range(len(point)):
        pygame.draw.circle(screen, BACK_COLOR,
                           (point[i][0], point[i][1]), point[i][2])


def snow():
    """
    |此函数需通过多线程或多进程启动，其在运行过程中会形成阻塞，无法使本函数之后的其他代码运行|
    本函数根据上述参数调节来绘制背景中的雪花
        注：此处绘制的雪花并非真正与现实相同，而是在背景中随机生成的圆点，其动画效果类似于雪花
    """
    while not done:
        snow_points = []  # 用于记录生成的雪花的点及雪花的半径，便于后续区域擦除
        for i in range(SNOW_NUM_MAX):  # 生成设置数量的雪花
            random_x = random.randint(0, WINDOW_SIZE[0])  # 随机生成雪花的横坐标
            random_y = random.randint(0, WINDOW_SIZE[1])  # 随机生成雪花的纵坐标
            random_color = random.choice(SNOW_RANDOM_COLORS)  # 随机生成雪花的颜色
            random_snow_size = random.randint(
                SNOW_SIZE_MIN, SNOW_SIZE_MAX)  # 随机雪花的半径
            # 将随机出来的坐标以及大小存储在列表中
            snow_points.append([random_x, random_y, random_snow_size])
            pygame.draw.circle(screen, random_color, [
                               random_x, random_y], random_snow_size)  # 在背景上绘制雪花
        pygame.display.update()  # 刷新窗口
        # draw_teee()
        sleep(SNOW_TIME)  # 延迟设定的时间后变化
        clean_snow(snow_points)  # 清除绘制的点
    pygame.quit()


def change_Ball_color():
    """
    |此函数需通过多线程或多进程启动，其在运行过程中会形成阻塞，无法使本函数之后的其他代码运行|
    此函数是用于更改圣诞树上悬挂的球的颜色
    """
    global RANDOM_COLOR, done  # 将前面生命的变量全局应用
    while not done:
        for i in range(len(TREE_BALL_COLORS)):
            RANDOM_COLOR = TREE_BALL_COLORS[i]  # 更改颜色
            sleep(BALL_CHANGE_CALOR_TIME)  # 延迟设定时长
    pygame.quit()


def tree_base():
    """
    |此函数需通过多线程或多进程启动，其在运行过程中会形成阻塞，无法使本函数之后的其他代码运行|
    此函数是用于绘制圣诞树底座
    """
    while not done:
        pt = []  # 用于记录绘制的圆的位置
        for i in range(BASE_BALL_NUM):  # 根据参数设置进行定量循环绘制
            random_x = random.randint(int(TREE_TOP_POINT[0]-ANGLE*TREE_WEITH*(
                TREE_LOOP_NUM+1)+BASE_WIDTH_NUM), int(TREE_TOP_POINT[0]+ANGLE*TREE_WEITH*(TREE_LOOP_NUM+1)-BASE_WIDTH_NUM))  # 在横坐标的规定范围内随机横坐标来确定绘制的小球的横坐标位置
            random_y = random.randint(
                TREE_BOTTOM_POINT[1], TREE_BOTTOM_POINT[1] + BASE_THICKNESS)  # 在给定的纵坐标范围内，随机生成用于控制绘制的小球的纵坐标的位置
            pt.append([random_x, random_y, BASE_RADIUS])  # 将随机的点位和球的设定大小记录下来
            random_color = random.choice(BASE_COLORS)  # 在给定的颜色集合内随机颜色
            pygame.draw.circle(screen, random_color, [
                               random_x, random_y], BASE_RADIUS)  # 根据上述参数绘制圆
        sleep(BASE_CHANGE_TIME)  # 延迟设定参数的时长（单位：s）
        clean_snow(pt)  # 清除上次绘制的以便下次绘制


def draw_font():
    """
    此函数用于根据给定的文字和字体绘制文字
    """
    # ========================圣诞树底座的文字的绘制========================
    font = pygame.font.Font(FONT, FONT_SIZE)  # 设置文字字体
    text_size = pygame.font.Font.size(font, TEXT)  # 设置文字大小
    text_pos = [TEXT_CENTER_POINT[0]-text_size[0] /
                2, TEXT_CENTER_POINT[1]-text_size[1]/2]  # 计算文字位置
    text = pygame.font.Font.render(font, TEXT, True, TEXT_COLOR)  # 绘制文字
    screen.blit(text, text_pos)  # 展示文字
    # ======================================================================

    # ========================窗口顶部的文字的绘制========================
    font_2 = pygame.font.Font(FONT, FONT_SIZE_2)
    text_size_2 = pygame.font.Font.size(font_2, TEXT_2)
    text_pos_2 = [TEXT_CENTER_POINT[0]-text_size_2[0]/2, 0]
    text_2 = pygame.font.Font.render(font_2, TEXT_2, True, TEXT_COLOR)
    screen.blit(text_2, text_pos_2)
    # ====================================================================


def play_music():
    """
    此函数用于播放背景音乐
    """
    pygame.mixer.init()  # 初始化音乐模块
    pygame.mixer.music.load(MUSIC)  # 加载设定音乐
    pygame.mixer.music.set_volume(0.5)  # 设置音量
    while not done:  # 循环播放音乐，直到窗口关闭（程序退出）
        if not pygame.mixer.music.get_busy():  # 检测音乐是否在播放，若不播放，则开始播放
            pygame.mixer.music.play()  # 播放音乐
    pygame.quit()


# ==========================各个线程全部启动==========================
_thread.start_new_thread(change_Ball_color, ())
_thread.start_new_thread(tree_base, ())
_thread.start_new_thread(draw_teee, ())
_thread.start_new_thread(snow, ())
_thread.start_new_thread(play_music, ())
# =====================================================================

while not done:
    # draw_teee()
    for event in pygame.event.get():  # 事件监听
        if event.type == pygame.QUIT:  # 若按下窗口的退出键
            # done = True
            pygame.quit()  # pygame退出
            sys.exit(0)  # 所有程序退出
